﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ArtheaEngine;
using ArtheaEngine.Model;
using System.Reflection;
using System.Runtime.Serialization;

namespace ArtheaServer.Creation
{
    public abstract class Editor
    {
        public Editor Next { get; set; }

        protected Connection _connection;
        public Editor(Connection conn) { _connection = conn; }
        public virtual void Execute(Argument argument)
        {
            if (!argument || argument == "show")
            {
                Show();
                return;
            }

            if (argument[0] == '?' || argument == "help")
            {
                Help();
                return;
            }
        }
        public abstract void Show();
        public abstract void Help();
    }

    public class TextEditor : Editor
    {
        private StringBuilder buf;
        private object _data;
        private PropertyInfo _prop;

        public TextEditor(Connection conn, object data, PropertyInfo prop)
            : base(conn)
        {
            _prop = prop;
            _data = data;
            buf = new StringBuilder(prop.GetValue(data, null).ToString());

            Show();
        }

        public override void Execute(Argument argument)
        {
            if(argument.Length > 1 && argument[0] == '/' && argument[1] != '/')
            {
                var arg = argument.Next();

                switch (arg)
                {
                    default:
                        _connection.WriteLine("Huh?");
                        break;
                    case "/q":
                        _connection.Editing = Next;
                        _prop.SetValue(_data, buf.ToString(), null);
                        break;
                    case "/c":
                        buf = new StringBuilder();
                        _connection.WriteLine("Text cleared.");
                        break;
                    case "/s":
                        Show();
                        break;
                    case "/S":
                        _connection.Page(buf);
                        break;
                    case "/r":
                        var arg1 = argument.Next();

                        if (string.IsNullOrEmpty(arg1))
                        {
                            _connection.WriteLine("Usage: /r 'old string' 'new string'");
                        }
                        else
                        {
                            var arg2 = argument.Next();

                            buf = buf.Replace(arg1, arg2);
                            _connection.WriteLine("'{0}' replaced with '{1}'.", arg1, arg2);
                        }
                        break;
                    case "/e":
                        int line;

                        if (!int.TryParse(argument.Next(), out line))
                        {
                            _connection.WriteLine("You must specify a line number.");
                            break;
                        }

                        buf = LineReplace(line, argument);
                        _connection.WriteLine("Line replaced.");
                        break;
                    case "/i":
                        if (!int.TryParse(argument.Next(), out line))
                        {
                            _connection.WriteLine("You must specify a line number.");
                            break;
                        }
                        buf = LineAdd(line, argument);
                        _connection.WriteLine("Line inserted.");
                        break;
                    case "/d":
                        if (int.TryParse(argument.Next(), out line))
                        {
                            buf = LineDelete(line);
                            _connection.WriteLine("Line {0} deleted.", line);
                        }
                        else
                        {
                            buf = DeleteLastLine();
                            _connection.WriteLine("Last line deleted.");
                        }
                        break;
                    case "h":
                        Help();
                        break;
                }
            }
            else
            {
                buf.AppendLine(argument);
            }
        }

        private StringBuilder DeleteLastLine()
        {
            string[] lines = buf.ToString().GetLines();

            StringBuilder newText = new StringBuilder();

            for (int i = 0; i < lines.Length - 2; i++)
            {
                newText.AppendLine(lines[i]);
            }
            return newText;
        }

        private StringBuilder LineAdd(int line, String addStr)
        {
            string[] lines = buf.ToString().GetLines();
            StringBuilder newText = new StringBuilder();

            for (int i = 0; i < lines.Length - 1; i++)
            {
                if ((i + 1) == line)
                    newText.AppendLine(addStr);

                newText.AppendLine(lines[i]);
            }

            return newText;
        }

        private StringBuilder LineDelete(int line)
        {
            string[] lines = buf.ToString().GetLines();
            StringBuilder newText = new StringBuilder();

            for (int i = 0; i < lines.Length - 1; i++)
            {
                if ((i + 1) != line)
                {
                    newText.AppendLine(lines[i]);
                }
            }

            return newText;
        }

        private StringBuilder LineReplace(int line, String addStr)
        {
            string[] lines = buf.ToString().GetLines();
            StringBuilder newText = new StringBuilder();

            for (int i = 0; i < lines.Length - 1; i++)
            {
                if ((i + 1) == line)
                    newText.AppendLine(addStr);
                else
                    newText.AppendLine(lines[i]);
            }

            return newText;
        }

        public override void Show()
        {
            string[] lines = buf.ToString().GetLines();

            if (lines.Length <= 1)
            {
                _connection.WriteLine();
                return;
            }

            for (int i = 0; i < lines.Length - 1; i++)
            {
                _connection.WriteLine("{0,2}. {1}", i + 1, lines[i]);
            }
        }

        public override void Help()
        {
            _connection.WriteLine("Commands:");
            _connection.WriteLine("/h               - displays this help");
            _connection.WriteLine("/e <line> [text] - replaces line number with text");
            _connection.WriteLine("/i <line> [text] - inserts text at line number");
            _connection.WriteLine("/d [line]        - deletes the last line or the specified line");
            _connection.WriteLine("/r <old> [new]   - replaces old string with new string");
            _connection.WriteLine("/c               - clears the text");
            _connection.WriteLine("/s               - shows the text with line numbers");
            _connection.WriteLine("/S               - shows the text without line numbers");
            _connection.WriteLine("/q               - quits the editor");
        }
    }

    public class HelpEditor : Editor
    {
        private Help _data;

        public HelpEditor(Connection conn, Help help) : base(conn)
        {
            _data = help;
        }

        public override void Execute(Argument argument)
        {
            base.Execute(argument);

            var arg = argument.Next();

            switch (arg.ToLower())
            {
                default:
                    _connection.WriteLine("Huh?");
                    break;
                case "":
                    Show();
                    break;
                case "text":
                    var editor = new TextEditor(_connection, _data, PropertyHelper<Help>.GetProperty(x => x.Text));
                    editor.Next = this;
                    _connection.Editing = editor;
                    break;
                case "keywords":
                    break;
            }
        }

        public override void Help()
        {
            string[] commands = new [] { "text", "keywords" };

            Columns.Show(_connection.Playing, 4, commands);
        }

        public override void Show()
        {
            var buf = new List<string>();

            buf.Add(string.Format("Keywords: {0}", _data.TagCSV()));
        }

    }

    public class RoomEditor : Editor
    {
        private Room _data;

        public RoomEditor(Connection conn, Room data)
            : base(conn)
        {
            _data = data;
        }

        public override void Execute(Argument argument)
        {
            base.Execute(argument);

            var arg = argument.Next();

            if (arg.IsPrefixOf("name"))
            {
                _data.Name = argument;
                Show();
                return;
            }

            if (arg.IsPrefixOf("description"))
            {
                var tmp = new TextEditor(_connection, _data, PropertyHelper<Room>.GetProperty(x => x.Description));
                tmp.Next = this;
                _connection.Editing = tmp;
                tmp.Show();
                return;
            }

            if (arg.IsPrefixOf("terrain"))
            {
                Terrain value;
                if (!Enum.TryParse(argument, out value))
                {
                    _connection.WriteLine("Invalid terrain.");
                    return;
                }

                _data.Terrain = value;
                Show();
            }

            Help();
        }

        public override void Help()
        {
            var commands = new string[] { "name", "description", "terrain" };

            Columns.Show(_connection.Playing, 4, commands);
        }

        public override void Show()
        {
            var buf = new List<string>();

            buf.Add(string.Format("Name: {0}", _data.Name));
            buf.Add(string.Format("Description: {0}", _data.Description));
            buf.Add(string.Format("Terrain: {0}", _data.Terrain.ToString()));

            Columns.Show(_connection.Playing, 2, buf);
        }
    }

    public class CharEditor : Editor
    {
        private Character _data;

        public CharEditor(Connection conn, Character data)
            : base(conn)
        {
            _data = data;
        }

        public override void Execute(Argument argument)
        {
            base.Execute(argument);

            var arg = argument.Next();

            if (arg.IsPrefixOf("name"))
            {
                _data.Name = argument;
                Show();
                return;
            }

            if (arg.IsPrefixOf("sex"))
            {
                Sex sex;
                if (!Enum.TryParse(argument, out sex))
                {
                    _connection.WriteLine("Invalid sex.");
                    return;
                }
                _data.Sex = sex;
                Show();
                return;
            }
            Help();
        }

        public override void Help()
        {
            var commands = new string[] { "name", "sex" };

            Columns.Show(_connection.Playing, 4, commands);
        }

        public override void Show()
        {
            List<string> buf = new List<string>();

            buf.Add(string.Format("Name: {0}", _data.Name));
            buf.Add(string.Format("Sex: {0}", _data.Sex));

            Columns.Show(_connection.Playing, 2, buf);
        }
    }
}
